﻿const im =
{
    /** @param {import('vue').App} vue */
    install: (vue) => vue.config.globalProperties.im = im,
    /** 后端接口的URL */
    url:  '',
    /** 状态码对应错误信息，错误码和错误信息的对象会回调给event.onerror事件 */
    error: { 0: '请求已中止' },
    /**
    * @typedef {Object} xhr_ex
    * @property {string} url - 接口的相对 url
    * @property {XMLHttpRequestBodyInit} data - 发送接口的数据
    * @property {('POST' | 'GET' | 'PUT' | 'DELETE')} method - 发送接口的方法
    * @property {function():Promise} resend - 重发接口，并返回重发的结果来完成 Promise
    */
    /** @typedef {XMLHttpRequest & xhr_ex} xhr */
    /** 请求结果
    * @callback RequestResult
    * @param {{ errCode: number, errMsg: string } | string} data - 请求返回的结果
    * @param {xhr} [xhr] - 重发请求
    * @param {function(any)} [resolve] - 回复成功
    * @param {function(any)} [reject] - 回复失败
    * @returns {Promise<any | undefined> | (any | undefined)} 返回非空则不会继续后面的处理
    */
    /**
    * @typedef {Object} RequestEvent
    * @property {function(xhr)} onopen - 请求准备发送时的事件
    * @property {function(xhr)} onloading - 请求正在加载的事件(readyState = 3)
    * @property {function(xhr)} oncomplete - 请求完成时的事件(readyState = 4)
    * @property {RequestResult} onsuccess - 请求成功的事件(readyState = 4 && status == 200)
    * @property {RequestResult} onerror - 请求发生错误时的事件(readyState = 4 && status != 200)
    * @property {function(ProgressEvent)} onprogress - 上传文件请求的进度事件
    */
    /** 接口请求的全局事件
    * @type {RequestEvent} */
    __event: undefined,
    /** 接口请求的全局事件
    * @type {RequestEvent} */
    event: undefined,
    /** 本次接口请求的临时事件，onprogress以外的全局事件任然会触发，临时事件优先于全局事件触发
    * @param {RequestEvent} event */
    eventonce(event)
    {
        this.__event = Object.assign({}, this.__event, event);
        return this;
    },
    /** 发送接口
    * @param {String} url - 接口url
    * @param {XMLHttpRequestBodyInit} [data] - 数据，可以是FormData或a=1&b=2格式的字符串
    * @param {('POST' | 'GET')} method - 默认POST
    */
    send(url, data, method = 'POST')
    {
        return new Promise((resolve, reject) =>
        {
            const req = new XMLHttpRequest();
            const event = this.__event;
            this.__event = undefined;
            req.url = url;
            req.data = data;
            req.method = method;
            req.resend = () => { return this.eventonce(event).send(url, data, method).then(i => resolve(i)).catch(i => reject(i)); };
            req.onreadystatechange = async () =>
            {
                if (req.readyState == 4)
                {
                    event?.oncomplete?.(req) | this.event?.oncomplete?.(req);
                    if (req.status == 200)
                    {
                        let obj = null;
                        switch (req.responseType)
                        {
                            case 'text': if (req.response) obj = JSON.parse(req.responseText); break;
                            case 'blob':
                            if (req.response.type == 'text/plain') await req.response.text().then((value) => obj = JSON.parse(value));
                            else obj = req.response;
                            break;
                        }
                        if (obj && obj.errCode)
                        await event?.onerror?.(obj, req, resolve, reject) || await this.event?.onerror?.(obj, req, resolve, reject) | reject(obj);
                        else
                        await event?.onsuccess?.(obj, req, resolve, reject) || await this.event?.onsuccess?.(obj, req, resolve, reject) | resolve(obj);
                    }
                    else
                    {
                        let error = { errCode: req.status, errMsg: '' };
                        error.errMsg = this.error[req.status] || req.statusText || req.responseText;
                        await event?.onerror?.(error, req, resolve, reject) || this.event?.onerror?.(error, req, resolve, reject) || reject(error);
                    }
                }
                else if (req.readyState == 3) event?.onloading?.(req) | this.event?.onloading?.(req);
            }
            req.open(method, this.url + url, true);
            req.upload.onprogress = event?.onprogress || this.event?.onprogress;
            req.responseType = 'text';
            if (event?.onopen) event.onopen(req);
            else if (this.event?.onopen) this.event.onopen(req);
            else req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
            req.send(data);
        });
    },
    /** 调用下载文件的接口前调用
    * @param {string} filename - 下载文件的名字，例如download.csv
    * @example
    * api.download('download.csv').downloadCSV()
    */
    download(filename)
    {
        return this.eventonce(
        {
            oncomplete(req)
            {
                const a = window.document.createElement('a');
                a.href = URL.createObjectURL(req.response);
                a.download = filename;
                a.click();
            }
        })
    },
    /**
    * @param {String | Connect} username
    * @param {String} password
    * @returns {Promise<String>}
    */
    Connect(username, password)
    {
        arguments.length == 1 && ({ username, password } = arguments[0])
        const data = [];
        if (username) data.push("username=" + encodeURIComponent(username));
        if (password) data.push("password=" + encodeURIComponent(password));
        return this.send("192/Connect", data.join('&'));
    },
    /**
    * @param {ServiceType} type
    * @returns {Promise<any>}
    */
    ModifyServiceType(type)
    {
        const data = [];
        if (type) data.push("type=" + encodeURIComponent(JSON.stringify(type)));
        return this.send("192/ModifyServiceType", data.join('&'));
    },
    /**
    * @param {String} name
    * @returns {Promise<any>}
    */
    DeleteServiceType(name)
    {
        const data = [];
        if (name) data.push("name=" + encodeURIComponent(name));
        return this.send("192/DeleteServiceType", data.join('&'));
    },
    /**
    * @returns {Promise<any>}
    */
    GetServiceType()
    {
        return this.send("192/GetServiceType");
    },
    /**
    * @returns {Promise<any>}
    */
    GetServers()
    {
        return this.send("192/GetServers");
    },
    /**
    * @returns {Promise<any>}
    */
    UpdateServer()
    {
        return this.send("192/UpdateServer");
    },
    /**
    * @param {Number | NewService} serverID
    * @param {String} serviceType
    * @param {String} name
    * @param {String} exe
    * @param {String} command
    * @returns {Promise<Boolean>}
    */
    NewService(serverID, serviceType, name, exe, command)
    {
        arguments.length == 1 && ({ serverID, serviceType, name, exe, command } = arguments[0])
        const data = [];
        if (serverID) data.push("serverID=" + serverID);
        if (serviceType) data.push("serviceType=" + encodeURIComponent(serviceType));
        if (name) data.push("name=" + encodeURIComponent(name));
        if (exe) data.push("exe=" + encodeURIComponent(exe));
        if (command) data.push("command=" + encodeURIComponent(command));
        return this.send("192/NewService", data.join('&'));
    },
    /**
    * @param {String[] | SetServiceLaunchCommand} serviceNames
    * @param {String} exe
    * @param {String} command
    * @returns {Promise<Boolean>}
    */
    SetServiceLaunchCommand(serviceNames, exe, command)
    {
        arguments.length == 1 && ({ serviceNames, exe, command } = arguments[0])
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        if (exe) data.push("exe=" + encodeURIComponent(exe));
        if (command) data.push("command=" + encodeURIComponent(command));
        return this.send("192/SetServiceLaunchCommand", data.join('&'));
    },
    /**
    * @param {String} serviceName
    * @returns {Promise<any>}
    */
    GetCommands(serviceName)
    {
        const data = [];
        if (serviceName) data.push("serviceName=" + encodeURIComponent(serviceName));
        return this.send("192/GetCommands", data.join('&'));
    },
    /**
    * @param {String[] | CallCommand} serviceNames
    * @param {String} command
    * @returns {Promise<Boolean>}
    */
    CallCommand(serviceNames, command)
    {
        arguments.length == 1 && ({ serviceNames, command } = arguments[0])
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        if (command) data.push("command=" + encodeURIComponent(command));
        return this.send("192/CallCommand", data.join('&'));
    },
    /**
    * @param {String[]} serviceNames
    * @returns {Promise<any>}
    */
    DeleteService(serviceNames)
    {
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        return this.send("192/DeleteService", data.join('&'));
    },
    /**
    * @param {String[]} serviceNames
    * @returns {Promise<any>}
    */
    LaunchService(serviceNames)
    {
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        return this.send("192/LaunchService", data.join('&'));
    },
    /**
    * @param {String[]} serviceNames
    * @returns {Promise<any>}
    */
    UpdateService(serviceNames)
    {
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        return this.send("192/UpdateService", data.join('&'));
    },
    /**
    * @param {String[]} serviceNames
    * @returns {Promise<any>}
    */
    StopService(serviceNames)
    {
        const data = [];
        if (serviceNames) data.push("serviceNames=" + encodeURIComponent(JSON.stringify(serviceNames)));
        return this.send("192/StopService", data.join('&'));
    },
    /**
    * @param {Manager} manager
    * @returns {Promise<any>}
    */
    NewManager(manager)
    {
        const data = [];
        if (manager) data.push("manager=" + encodeURIComponent(JSON.stringify(manager)));
        return this.send("192/NewManager", data.join('&'));
    },
    /**
    * @param {String} name
    * @returns {Promise<any>}
    */
    DeleteManager(name)
    {
        const data = [];
        if (name) data.push("name=" + encodeURIComponent(name));
        return this.send("192/DeleteManager", data.join('&'));
    },
    /**
    * @returns {Promise<any>}
    */
    GetManagers()
    {
        return this.send("192/GetManagers");
    },
    /**
    * @param {String | GetLog} name
    * @param {String?} start
    * @param {String?} end
    * @param {Number} pageCount
    * @param {Number} page
    * @param {String} content
    * @param {String} param
    * @param {Number[]} levels
    * @returns {Promise<PagedModel<LogRecord>>}
    */
    GetLog(name, start, end, pageCount, page, content, param, levels)
    {
        arguments.length == 1 && ({ name, start, end, pageCount, page, content, param, levels } = arguments[0])
        const data = [];
        if (name) data.push("name=" + encodeURIComponent(name));
        if (start) data.push("start=" + encodeURIComponent(JSON.stringify(start)));
        if (end) data.push("end=" + encodeURIComponent(JSON.stringify(end)));
        if (pageCount) data.push("pageCount=" + pageCount);
        if (page) data.push("page=" + page);
        if (content) data.push("content=" + encodeURIComponent(content));
        if (param) data.push("param=" + encodeURIComponent(param));
        if (levels) data.push("levels=" + encodeURIComponent(JSON.stringify(levels)));
        return this.send("192/GetLog", data.join('&'));
    },
    /**
    * @param {String | GroupLog} name
    * @param {String?} start
    * @param {String?} end
    * @param {String} content
    * @param {String} param
    * @param {Number[]} levels
    * @returns {Promise<PagedModel<LogRecord>>}
    */
    GroupLog(name, start, end, content, param, levels)
    {
        arguments.length == 1 && ({ name, start, end, content, param, levels } = arguments[0])
        const data = [];
        if (name) data.push("name=" + encodeURIComponent(name));
        if (start) data.push("start=" + encodeURIComponent(JSON.stringify(start)));
        if (end) data.push("end=" + encodeURIComponent(JSON.stringify(end)));
        if (content) data.push("content=" + encodeURIComponent(content));
        if (param) data.push("param=" + encodeURIComponent(param));
        if (levels) data.push("levels=" + encodeURIComponent(JSON.stringify(levels)));
        return this.send("192/GroupLog", data.join('&'));
    },
};
export default im
/**
* @typedef {Object} ServiceType
* @property {String} Name
* @property {String} SVNPath
* @property {String} SVNUser
* @property {String} SVNPassword
* @property {String} Exe
* @property {String} LaunchCommand
* @property {Number} Revision
*/
/**
* @typedef {Object} Server
* @property {Number} ID
* @property {String} EndPoint
* @property {String} NickName
* @property {Service[]} Services
*/
/**
* @typedef {Object} Service
* @property {String} Name
* @property {String} Type
* @property {Number} Revision
* @property {String} LaunchCommand
* @property {String} Exe
* @property {Number} RevisionOnServer
* @property {String} LastStatusTime
* @property {EServiceStatus} Status
*/
/**
* @typedef {('0' | '1' | '2')} EServiceStatus
*/
export const EServiceStatus =
{
    Stop: 0,
    Starting: 1,
    Running: 2,
}
/**
* @typedef {Object} Manager
* @property {String} Name
* @property {String} Password
* @property {ESecurity} Security
*/
/**
* @typedef {('0' | '1' | '2' | '3')} ESecurity
*/
export const ESecurity =
{
    Programmer: 0,
    Maintainer: 1,
    Manager: 2,
    Administrator: 3,
}
/**
* @template T
* @typedef {Object} PagedModel
* @property {Number} Count
* @property {Number} Page
* @property {Number} PageSize
* @property {Number} TotalPage
* @property {T[]} Models
*/
/**
* @typedef {Object} LogRecord
* @property {Record} Record
* @property {Number} Count
*/
/**
* @typedef {Object} Record
* @property {Number} Level
* @property {String} Time
* @property {String} Content
* @property {String[]} Params
*/
/**
* @typedef Connect
* @property {String} username
* @property {String} password
*/
/**
* @typedef NewService
* @property {Number} serverID
* @property {String} serviceType
* @property {String} name
* @property {String} exe
* @property {String} command
*/
/**
* @typedef SetServiceLaunchCommand
* @property {String[]} serviceNames
* @property {String} exe
* @property {String} command
*/
/**
* @typedef CallCommand
* @property {String[]} serviceNames
* @property {String} command
*/
/**
* @typedef GetLog
* @property {String} name
* @property {String?} start
* @property {String?} end
* @property {Number} pageCount
* @property {Number} page
* @property {String} content
* @property {String} param
* @property {Number[]} levels
*/
/**
* @typedef GroupLog
* @property {String} name
* @property {String?} start
* @property {String?} end
* @property {String} content
* @property {String} param
* @property {Number[]} levels
*/

